package com.chucang.shucang.oms.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chucang.shucang.common.base.constant.SecurityConstant;
import com.chucang.shucang.common.base.exception.FastException;
import com.chucang.shucang.common.base.page.PageData;
import com.chucang.shucang.common.base.page.PageQuery;
import com.chucang.shucang.common.base.utils.OpsUtil;
import com.chucang.shucang.common.base.utils.R;
import com.chucang.shucang.oms.constant.OmsCommonConstant;
import com.chucang.shucang.oms.entity.BlindBoxOrderBaseEntity;
import com.chucang.shucang.oms.entity.CollectionOrderBaseEntity;
import com.chucang.shucang.oms.feign.PmsService;
import com.chucang.shucang.oms.feign.vo.BlindBoxBaseEntity;
import com.chucang.shucang.oms.feign.vo.CollectionBaseEntity;
import com.chucang.shucang.oms.service.BaseOrderService;
import com.chucang.shucang.oms.service.BlindBoxOrderBaseService;
import com.chucang.shucang.oms.service.CollectionOrderBaseService;
import com.chucang.shucang.oms.vo.BaseOrderPageResVO;
import com.chucang.shucang.oms.vo.query.BaseOrderQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;


/**
 * 藏品基础订单表
 *
 * @author flitsneak flitsneak@gmail.com
 * @since 1.0.0 2022-09-12
 */
@Service
@RequiredArgsConstructor
public class BaseOrderServiceImpl implements BaseOrderService {

    private final BlindBoxOrderBaseService blindBoxOrderBaseService;

    private final CollectionOrderBaseService collectionOrderBaseService;

    private final PmsService pmsService;

    private final ThreadPoolTaskExecutor omsExecutor;

    private final int ALL = -1;
    private final int WAITING_FOR_PAY = 0;
    private final int PAID = 1;

    @Override
    public PageData<BaseOrderPageResVO> orderPage(BaseOrderQuery baseOrderQuery) throws ExecutionException, InterruptedException {
        int page = 1;
        int limit = baseOrderQuery.getLimit() * baseOrderQuery.getPage();
        int rawPage = baseOrderQuery.getPage();
        int rawLimit = baseOrderQuery.getLimit();
        int offsetBegin = (rawPage - 1) * rawLimit;

        baseOrderQuery.setPage(page);
        baseOrderQuery.setLimit(limit);

        CompletableFuture<IPage<CollectionOrderBaseEntity>> collectionFuture = CompletableFuture.supplyAsync(() -> {
            LambdaQueryWrapper<CollectionOrderBaseEntity> collectionOrderQueryWrapper = new LambdaQueryWrapper<>();
            collectionOrderQueryWrapper
                    .eq(CollectionOrderBaseEntity::getUserId, baseOrderQuery.getId())
                    .orderByDesc(CollectionOrderBaseEntity::getUpdateTime);
            if (baseOrderQuery.getStatus() == WAITING_FOR_PAY) {
                collectionOrderQueryWrapper.eq(CollectionOrderBaseEntity::getStatus, OmsCommonConstant.WAITING_FOR_PAY);
            } else if (baseOrderQuery.getStatus() == PAID) {
                collectionOrderQueryWrapper.eq(CollectionOrderBaseEntity::getStatus, OmsCommonConstant.PAY_SUCCESS);
            }
            return collectionOrderBaseService.page(
                    new PageQuery<CollectionOrderBaseEntity>().getPage(baseOrderQuery),
                    collectionOrderQueryWrapper
            );
        }, omsExecutor);

        CompletableFuture<IPage<BlindBoxOrderBaseEntity>> blindBoxFuture = CompletableFuture.supplyAsync(() -> {
            LambdaQueryWrapper<BlindBoxOrderBaseEntity> blindBoxOrderQueryWrapper = new LambdaQueryWrapper<>();
            blindBoxOrderQueryWrapper
                    .eq(BlindBoxOrderBaseEntity::getUserId, baseOrderQuery.getId())
                    .orderByDesc(BlindBoxOrderBaseEntity::getUpdateTime);
            if (baseOrderQuery.getStatus() == OmsCommonConstant.WAITING_FOR_PAY) {
                blindBoxOrderQueryWrapper.eq(BlindBoxOrderBaseEntity::getStatus, OmsCommonConstant.WAITING_FOR_PAY);
            } else if (baseOrderQuery.getStatus() == PAID) {
                blindBoxOrderQueryWrapper.eq(BlindBoxOrderBaseEntity::getStatus, OmsCommonConstant.PAY_SUCCESS);
            }
            return blindBoxOrderBaseService.page(
                    new PageQuery<BlindBoxOrderBaseEntity>().getPage(baseOrderQuery),
                    blindBoxOrderQueryWrapper
            );
        }, omsExecutor);

        return collectionFuture.thenCombineAsync(blindBoxFuture, (cp, bp) -> {
                    //藏品set
                    Set<Long> collectionSet = new HashSet<>(32);
                    //盲盒set
                    Set<Long> blindBoxSet = new HashSet<>(32);
                    if (CollUtil.isEmpty(cp.getRecords()) && CollUtil.isEmpty(bp.getRecords())) {
                        return new PageData<BaseOrderPageResVO>(Collections.emptyList(), 0);
                    }
                    if ((cp.getTotal() + bp.getTotal()) < offsetBegin) {
                        return new PageData<BaseOrderPageResVO>(Collections.emptyList(), 0);
                    }
                    if (!CollUtil.isEmpty(cp.getRecords()) && !CollUtil.isEmpty(bp.getRecords())) {
                        List<BaseOrderPageResVO> baseOrderPageResVOList = new ArrayList<>(32);
                        this.collectCollectionInfo(baseOrderPageResVOList, cp.getRecords());
                        this.collectBlindBoxInfo(baseOrderPageResVOList, bp.getRecords());
                        baseOrderPageResVOList.sort(Comparator.comparing(BaseOrderPageResVO::getUpdateTime, Comparator.reverseOrder()));
                        List<BaseOrderPageResVO> baseOrderPageResVOS = baseOrderPageResVOList.subList(offsetBegin, Math.min(baseOrderPageResVOList.size(), limit));
                        //获取基础信息
                        baseOrderPageResVOS.forEach(c -> {
                            if (c.getProductType() == OmsCommonConstant.COLLECTION) {
                                collectionSet.add(c.getProductId());
                            } else if (c.getProductType() == OmsCommonConstant.BLIND_BOX) {
                                blindBoxSet.add(c.getProductId());
                            }
                        });
                        if (!CollUtil.isEmpty(collectionSet)) {
                            this.setCollectionOrderInfo(baseOrderPageResVOS, collectionSet);
                        }
                        if (!CollUtil.isEmpty(blindBoxSet)) {
                            this.setBlindBoxOrderInfo(baseOrderPageResVOS, blindBoxSet);
                        }
                        return new PageData<>(baseOrderPageResVOS, cp.getTotal() + bp.getTotal());
                    }
                    if (CollUtil.isEmpty(cp.getRecords())) {
                        List<BaseOrderPageResVO> baseOrderPageResVOList = new ArrayList<>(32);
                        this.collectBlindBoxInfo(baseOrderPageResVOList, bp.getRecords());
                        List<BaseOrderPageResVO> bpList = baseOrderPageResVOList.subList(offsetBegin, Math.min(baseOrderPageResVOList.size(), limit));
                        bpList.forEach(bb ->
                                blindBoxSet.add(bb.getProductId())
                        );
                        if (!CollUtil.isEmpty(blindBoxSet)) {
                            this.setBlindBoxOrderInfo(bpList, blindBoxSet);
                        }
                        return new PageData<>(bpList, cp.getTotal() + bp.getTotal());
                    }
                    if (CollUtil.isEmpty(bp.getRecords())) {
                        List<BaseOrderPageResVO> baseOrderPageResVOList = new ArrayList<>(32);
                        this.collectCollectionInfo(baseOrderPageResVOList, cp.getRecords());
                        List<BaseOrderPageResVO> bpList = baseOrderPageResVOList.subList(offsetBegin, Math.min(baseOrderPageResVOList.size(), limit));
                        bpList.forEach(bb ->
                                collectionSet.add(bb.getProductId())
                        );
                        if (!CollUtil.isEmpty(collectionSet)) {
                            this.setCollectionOrderInfo(bpList, collectionSet);
                        }
                        return new PageData<>(bpList, cp.getTotal() + bp.getTotal());
                    }
                    return new PageData<BaseOrderPageResVO>(Collections.emptyList(), 0);
                }, omsExecutor)
                .get();
    }

    private void setBlindBoxOrderInfo(List<BaseOrderPageResVO> baseOrderPageResVOS, Set<Long> blindBoxSet) {
        Map<Long, String> bbHashMap = new HashMap<>(32);
        Map<Long, String> bbCoverPicHashMap = new HashMap<>(32);
        Map<Long, Integer> bbTotalHashMap = new HashMap<>(32);
        blindBoxSet
                .stream()
                .map(bs -> CompletableFuture.supplyAsync(() -> {
                            R<BlindBoxBaseEntity> baseInfo = pmsService
                                    .getBlindBoxBaseInfo(bs, SecurityConstant.FROM_IN);
                            return OpsUtil
                                    .of(baseInfo)
                                    .assertDataNotNull(e -> new FastException("盲盒信息为空！"))
                                    .getData()
                                    .get();
                        }, omsExecutor
                ))
                .collect(Collectors.toList())
                .stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList())
                .forEach(bbs -> {
                    bbHashMap.put(bbs.getId(), bbs.getBbName());
                    bbCoverPicHashMap.put(bbs.getId(), bbs.getCoverPicture());
                    bbTotalHashMap.put(bbs.getId(), bbs.getTotalNum());
                });
        baseOrderPageResVOS.forEach(bop -> {
            if (bop.getProductType() == OmsCommonConstant.BLIND_BOX) {
                bop.setProductName(bbHashMap.get(bop.getProductId()));
                bop.setCoverPicture(bbCoverPicHashMap.get(bop.getProductId()));
            }
        });
    }

    private void setCollectionOrderInfo(List<BaseOrderPageResVO> baseOrderPageResVOS, Set<Long> collectionSet) {
        Map<Long, String> ccHashMap = new HashMap<>(32);
        Map<Long, String> ccCoverPicHashMap = new HashMap<>(32);
        Map<Long, Integer> ccTotalHashMap = new HashMap<>(32);
        collectionSet
                .stream()
                .map(cs -> CompletableFuture.supplyAsync(() -> {
                            R<CollectionBaseEntity> baseInfo = pmsService
                                    .getCollectionBaseInfo(cs, SecurityConstant.FROM_IN);
                            return OpsUtil
                                    .of(baseInfo)
                                    .assertDataNotNull(e -> new FastException("藏品信息为空！"))
                                    .getData()
                                    .get();
                        }, omsExecutor
                ))
                .collect(Collectors.toList())
                .stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList())
                .forEach(ccs -> {
                    ccHashMap.put(ccs.getId(), ccs.getDcName());
                    ccCoverPicHashMap.put(ccs.getId(), ccs.getCoverPicture());
                    ccTotalHashMap.put(ccs.getId(), ccs.getTotalNum());
                });
        baseOrderPageResVOS.forEach(cop -> {
            if (cop.getProductType() == OmsCommonConstant.COLLECTION) {
                cop.setProductName(ccHashMap.get(cop.getProductId()));
                cop.setCoverPicture(ccCoverPicHashMap.get(cop.getProductId()));
            }
        });
    }

    private void collectBlindBoxInfo(List<BaseOrderPageResVO> baseOrderPageResVOList, List<BlindBoxOrderBaseEntity> records) {
        records.forEach(bo -> {
            BaseOrderPageResVO baseOrderPageResVO = new BaseOrderPageResVO();
            baseOrderPageResVO.setOrderAmount(bo.getOrderAmount());
            baseOrderPageResVO.setOrderSn(bo.getOrderSn());
            baseOrderPageResVO.setCreateTime(bo.getCreateTime());
            baseOrderPageResVO.setUpdateTime(bo.getUpdateTime());
            baseOrderPageResVO.setPayType(bo.getPayType());
            baseOrderPageResVO.setStatus(bo.getStatus());
            baseOrderPageResVO.setOrderNum(bo.getOrderNum());
            baseOrderPageResVO.setProductType(OmsCommonConstant.BLIND_BOX);
            baseOrderPageResVO.setProductId(bo.getBbId());
            baseOrderPageResVOList.add(baseOrderPageResVO);
        });
    }

    private void collectCollectionInfo(List<BaseOrderPageResVO> baseOrderPageResVOList, List<CollectionOrderBaseEntity> records) {
        records.forEach(co -> {
            BaseOrderPageResVO baseOrderPageResVO = new BaseOrderPageResVO();
            baseOrderPageResVO.setOrderAmount(co.getOrderAmount());
            baseOrderPageResVO.setOrderSn(co.getOrderSn());
            baseOrderPageResVO.setCreateTime(co.getCreateTime());
            baseOrderPageResVO.setUpdateTime(co.getUpdateTime());
            baseOrderPageResVO.setPayType(co.getPayType());
            baseOrderPageResVO.setStatus(co.getStatus());
            baseOrderPageResVO.setOrderNum(co.getOrderNum());
            baseOrderPageResVO.setProductType(OmsCommonConstant.COLLECTION);
            baseOrderPageResVO.setProductId(co.getCollectionId());
            baseOrderPageResVOList.add(baseOrderPageResVO);
        });
    }
}